//
//  SocketRocketUtility.m
//  XTXOneToOne
//
//  Created by Canny on 2018/10/29.
//  Copyright © 2018年 WK. All rights reserved.
//

#import "SocketRocketUtility.h"
#import "HomeBaseModel.h"
#import "LeverageTradeModel.h"
#import "SDChatDataModel.h"
#import "SocketManager.h"
#import "K_LineDataModel.h"
#import "Y_KLineGroupModel.h"


#define dispatch_main_async_safe(block)\
if ([NSThread isMainThread]) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}



NSString * const kNeedPayOrderNote               = @"kNeedPayOrderNote";
NSString * const kWebSocketDidOpenNote           = @"kWebSocketdidReceiveMessageNote";
NSString * const kWebSocketDidCloseNote          = @"kWebSocketDidCloseNote";
NSString * const kWebSocketdidReceiveMessageNote = @"kWebSocketdidReceiveMessageNote";
NSString * const Chat_kWebSocketdidReceiveMessageNote = @"Chat_kWebSocketdidReceiveMessageNote";

NSString * const Kline_kWebSocketdidReceiveMessageNote = @"Kline_kWebSocketdidReceiveMessageNote";

@interface SocketRocketUtility()<SRWebSocketDelegate>
{
    int _index;
    NSTimer * heartBeat;
    NSTimeInterval reConnectTime;
}

@property (nonatomic,strong) SRWebSocket *socket;

@property (nonatomic,copy) NSString *urlString;

@end

@implementation SocketRocketUtility

+(SocketRocketUtility *)instance{
    static SocketRocketUtility *Instance = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        Instance = [[SocketRocketUtility alloc] init];
    });
    return Instance;
}

#pragma mark - **************** public methods
-(void)SRWebSocketOpenWithURLString:(NSString *)urlString {
    
    //如果是同一个url return
    if (self.socket) {
        return;
    }
    
    if (!urlString) {
        return;
    }
    
    self.urlString = urlString;
    
    self.socket = [[SRWebSocket alloc] initWithURLRequest:
                   [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]];
    
    NSLog(@"请求的websocket地址：%@",self.socket.url.absoluteString);
    
    self.socket.delegate = self;   //SRWebSocketDelegate 协议
    
    [self.socket open];     //开始连接
}

-(void)SRWebSocketClose{
    if (self.socket){
        [self.socket close];
        self.socket = nil;
        //断开连接时销毁心跳
        [self destoryHeartBeat];
    }
}

#define WeakSelf(ws) __weak __typeof(&*self)weakSelf = self
- (void)sendData:(id)data {
//    NSLog(@"socketSendData --------------- %@",data);
    
    WeakSelf(ws);
    dispatch_queue_t queue =  dispatch_queue_create("zy", NULL);
    
    dispatch_async(queue, ^{
        if (weakSelf.socket != nil) {
            // 只有 SR_OPEN 开启状态才能调 send 方法啊，不然要崩
            if (weakSelf.socket.readyState == SR_OPEN) {
                [weakSelf.socket send:data];    // 发送数据
                
            } else if (weakSelf.socket.readyState == SR_CONNECTING) {
//                NSLog(@"正在连接中，重连后其他方法会去自动同步数据");
                // 每隔2秒检测一次 socket.readyState 状态，检测 10 次左右
                // 只要有一次状态是 SR_OPEN 的就调用 [ws.socket send:data] 发送数据
                // 如果 10 次都还是没连上的，那这个发送请求就丢失了，这种情况是服务器的问题了，小概率的
                // 代码有点长，我就写个逻辑在这里好了
                [self reConnect];
                
            } else if (weakSelf.socket.readyState == SR_CLOSING || weakSelf.socket.readyState == SR_CLOSED) {
                // websocket 断开了，调用 reConnect 方法重连
                
//                NSLog(@"重连");
                
                [self reConnect];
            }
        } else {
//            NSLog(@"没网络，发送失败，一旦断网 socket 会被我设置 nil 的");
//            NSLog(@"其实最好是发送前判断一下网络状态比较好，我写的有点晦涩，socket==nil来表示断网");
        }
    });
}

#pragma mark - **************** private mothodes
//重连机制
- (void)reConnect
{
    [self SRWebSocketClose];
    
    //超过一分钟就不再重连 所以只会重连5次 2^5 = 64
    if (reConnectTime > 64) {
        //您的网络状况不是很好，请检查网络后重试
        return;
    }
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(reConnectTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.socket = nil;
        [self SRWebSocketOpenWithURLString:self.urlString];
//        NSLog(@"重连");
    });
    
    //重连时间2的指数级增长
    if (reConnectTime == 0) {
        reConnectTime = 2;
    }else{
        reConnectTime *= 2;
    }
}


//取消心跳
- (void)destoryHeartBeat
{
    dispatch_main_async_safe(^{
        if (heartBeat) {
            if ([heartBeat respondsToSelector:@selector(isValid)]){
                if ([heartBeat isValid]){
                    [heartBeat invalidate];
                    heartBeat = nil;
                }
            }
        }
    })
}

//初始化心跳
- (void)initHeartBeat
{
    dispatch_main_async_safe(^{
        [self destoryHeartBeat];
        //心跳设置为3分钟，NAT超时一般为5分钟
        heartBeat = [NSTimer timerWithTimeInterval:10 target:self selector:@selector(sentheart) userInfo:nil repeats:YES];
        //和服务端约定好发送什么作为心跳标识，尽可能的减小心跳包大小
        [[NSRunLoop currentRunLoop] addTimer:heartBeat forMode:NSRunLoopCommonModes];
    })
}

//获取当前时间戳有两种方法(以秒为单位)

-(NSString *)getNowTimeTimestamp{
    
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init] ;
    
    [formatter setDateStyle:NSDateFormatterMediumStyle];
    
    [formatter setTimeStyle:NSDateFormatterShortStyle];
    
    [formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"]; // ----------设置你想要的格式,hh与HH的区别:分别表示12小时制,24小时制
    
    //设置时区,这个对于时间的处理有时很重要
    
    NSTimeZone* timeZone = [NSTimeZone timeZoneWithName:@"Asia/Shanghai"];
    
    [formatter setTimeZone:timeZone];
    
    NSDate *datenow = [NSDate date];//现在时间,你可以输出来看下是什么格式
    
    NSString *timeSp = [NSString stringWithFormat:@"%ld", (long)[datenow timeIntervalSince1970]];
    
    return timeSp;
    
}
-(void)sentheart{
    //发送心跳 和后台可以约定发送什么内容  一般可以调用ping  我这里根据后台的要求 发送了data给他

   NSString * ts = [self getNowTimeTimestamp];
   NSData *data = [NSJSONSerialization dataWithJSONObject:@{
                                              @"type": @"ping",
                                              @"cmd": @"ping",
                                              @"ts":ts
                                              } options:NSJSONWritingPrettyPrinted error:nil];
    
     [self sendData:data];

}

//pingPong
- (void)ping{
    if (self.socket.readyState == SR_OPEN) {
        [self.socket sendPing:nil];
    }
}

#pragma mark - socket delegate
- (void)webSocketDidOpen:(SRWebSocket *)webSocket {
    //每次正常连接的时候清零重连时间
    reConnectTime = 0;
    //开启心跳
    [self initHeartBeat];
    if (webSocket == self.socket) {
//        NSLog(@"************************** socket 连接成功************************** ");
        [[NSNotificationCenter defaultCenter] postNotificationName:kWebSocketDidOpenNote object:nil];
    }
}

- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error {
    
    if (webSocket == self.socket) {
        NSLog(@"************************** socket 连接失败************************** ");
        _socket = nil;
        //连接失败就重连
        [self reConnect];
    }
}

- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean {

    if (webSocket == self.socket) {
        NSLog(@"************************** socket连接断开************************** ");
//        NSLog(@"被关闭连接，code:%ld,reason:%@,wasClean:%d",(long)code,reason,wasClean);
        [self SRWebSocketClose];
        [[NSNotificationCenter defaultCenter] postNotificationName:kWebSocketDidCloseNote object:nil];
    }
}

/*该函数是接收服务器发送的pong消息，其中最后一个是接受pong消息的，
 在这里就要提一下心跳包，一般情况下建立长连接都会建立一个心跳包，
 用于每隔一段时间通知一次服务端，客户端还是在线，这个心跳包其实就是一个ping消息，
 我的理解就是建立一个定时器，每隔十秒或者十五秒向服务端发送一个ping消息，这个消息可是是空的
 */
-(void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload{
//    NSString *reply = [[NSString alloc] initWithData:pongPayload encoding:NSUTF8StringEncoding];
//    NSLog(@"reply===%@",reply);
}

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message  {
    
    if (webSocket == self.socket) {
//        NSLog(@"************************** socket收到数据了************************** ");
//        NSLog(@"我这后台约定的 message 是 json 格式数据收到数据，就按格式解析吧，然后把数据发给调用层");
//        NSLog(@"message----didReceiveMessage------:%@",message);
        
        
        NSDictionary * dic = [SocketRocketUtility dictionaryWithJsonString:message];
        
        NSLog(@"--------dic--------%@",dic);
        HomeBaseModel * HB = [HomeBaseModel sharedHomeBaseModel];
        NSString * Type_Depth = [NSString stringWithFormat:@"Depth.L20.%@",HB.selectedModel.symbol];
        NSString * Type_Ticker = [NSString stringWithFormat:@"Ticker.%@",HB.selectedModel.symbol];
        NSString * Type_History = [NSString stringWithFormat:@"History.L20.%@",HB.selectedModel.symbol];


        
        
        NSString *type= dic[@"type"];
        if ([type isEqualToString:@"All-tickers"]) {
            NSArray * arr = dic[@"tickers"];
            if (![arr isKindOfClass:[NSArray class]]) {
                return;
            }
            for (NSDictionary * temp in arr) {
              
                NSString * pair_id = [NSString stringWithFormat:@"%@",temp[@"pair_id"]];
                for (HomeBaseTabCellModel * cModel in HB.BTC) {
                    NSString * ID = [NSString stringWithFormat:@"%@",cModel.ID];
                    if ([pair_id isEqualToString:ID]) {
                        cModel.margin=[NSString stringWithFormat:@"%@",temp[@"margin"]];
                        cModel.max_price=[MCMathUnits stringWithDecimalNumber:[temp[@"price"] doubleValue]];
                        cModel.min_price=[MCMathUnits stringWithDecimalNumber:[temp[@"min_price"] doubleValue]];
                        cModel.price=[MCMathUnits stringWithDecimalNumber:[temp[@"price"] doubleValue]];
                        cModel.symbol=[NSString stringWithFormat:@"%@",temp[@"symbol"]];
                        cModel.time=[NSString stringWithFormat:@"%@",temp[@"time"]];
                        cModel.volume=[NSString stringWithFormat:@"%@",temp[@"volume"]];
                        cModel.pair_id=[NSString stringWithFormat:@"%@",temp[@"pair_id"]];
                        
                        break;
                    }
                }
                
                for (HomeBaseTabCellModel * cModel in HB.ETH) {
                    NSString * ID = [NSString stringWithFormat:@"%@",cModel.ID];
                    if ([pair_id isEqualToString:ID]) {
                        cModel.margin=[NSString stringWithFormat:@"%@",temp[@"margin"]];
                        cModel.max_price=[MCMathUnits stringWithDecimalNumber:[temp[@"max_price"] doubleValue]];
                        cModel.min_price=[MCMathUnits stringWithDecimalNumber:[temp[@"min_price"] doubleValue]];
                        cModel.price=[MCMathUnits stringWithDecimalNumber:[temp[@"price"] doubleValue]];
                        cModel.symbol=[NSString stringWithFormat:@"%@",temp[@"symbol"]];
                        cModel.time=[NSString stringWithFormat:@"%@",temp[@"time"]];
                        cModel.volume=[NSString stringWithFormat:@"%@",temp[@"volume"]];
                        cModel.pair_id=[NSString stringWithFormat:@"%@",temp[@"pair_id"]];
                        
                        break;
                    }
                }
                for (HomeBaseTabCellModel * cModel in HB.USDT) {
                    NSString * ID = [NSString stringWithFormat:@"%@",cModel.ID];
                    if ([pair_id isEqualToString:ID]) {
                        cModel.margin=[NSString stringWithFormat:@"%@",temp[@"margin"]];
                        cModel.max_price=[NSString stringWithFormat:@"%@",temp[@"max_price"]];
                        cModel.min_price=[NSString stringWithFormat:@"%@",temp[@"min_price"]];
                        cModel.price=[NSString stringWithFormat:@"%@",temp[@"price"]];
                        cModel.symbol=[NSString stringWithFormat:@"%@",temp[@"symbol"]];
                        cModel.time=[NSString stringWithFormat:@"%@",temp[@"time"]];
                        cModel.volume=[NSString stringWithFormat:@"%@",temp[@"volume"]];
                        cModel.pair_id=[NSString stringWithFormat:@"%@",temp[@"pair_id"]];
                        break;
                    }
                }
            }
            [[NSNotificationCenter defaultCenter] postNotificationName:kWebSocketdidReceiveMessageNote object:message];

            
        }else if ([type containsString:Type_Depth]){
            
            NSDictionary * depth = dic[@"depth"];
            LeverageTradeModel * LTmodel =[[LeverageTradeModel alloc]init];
            LTmodel.asks_Bids_Model = [LTAsks_Bids_Model mj_objectWithKeyValues:depth];
 #pragma mark-排序
            
            [self sortingForSelectWithArray:LTmodel.asks_Bids_Model.asks];
            [self sortingForSelectWithArray:LTmodel.asks_Bids_Model.bids];
            
            [[NSNotificationCenter defaultCenter] postNotificationName:kWebSocketdidReceiveMessageNote object:message];


        }else if([type containsString:Type_Ticker]){
            
            NSDictionary * ticker = dic[@"ticker"];
            LeverageTradeModel * LTmodel =[[LeverageTradeModel alloc]init];
            LTmodel.tickerModel = [NowTickerModel mj_objectWithKeyValues:ticker];
            
            [[NSNotificationCenter defaultCenter] postNotificationName:kWebSocketdidReceiveMessageNote object:message];

            
        }else if([type containsString:@"error"]){
            
        }else if([type containsString:@"hello"]){
            
        }else if([type containsString:@"history"]){
            
            NSArray * history = dic[@"history"];
            SDChatDataModel * SD = [SDChatDataModel sharedSDChatDataModel];
            [SD.dataSource removeAllObjects];
            for (NSDictionary *dic in history){
                
                SDChatMessage *msg =[SDChatMessage chatMessageWithDic:dic];
                SDChatDetail *chat =[SDChatDetail sd_chatWith:msg];
                SDChatDetailFrame *chatFrame =[[SDChatDetailFrame alloc]init];
                chatFrame.chat=chat;
                [SD.dataSource addObject:chatFrame];
            }
            
            [[NSNotificationCenter defaultCenter] postNotificationName:Chat_kWebSocketdidReceiveMessageNote object:message];

            
            
        }else if ([type containsString:@"message"]){
            NSString * content = [NSString stringWithFormat:@"%@",dic[@"content"]];
            
            NSDictionary * msgDic = @{@"content":content,@"id":@"1",@"sender":@"0",@"create_time":[MOLoadHttpsData GetTime:[dic[@"ts"] integerValue]],@"msgType":@"0",@"type":@"0"};
            SDChatDataModel * SD = [SDChatDataModel sharedSDChatDataModel];
            SDChatMessage *msg =[SDChatMessage chatMessageWithDic:msgDic];
            SDChatDetail *chat =[SDChatDetail sd_chatWith:msg];
            SDChatDetailFrame *chatFrame =[[SDChatDetailFrame alloc]init];
            chatFrame.chat=chat;
            [SD.dataSource addObject:chatFrame];
            
            [[NSNotificationCenter defaultCenter] postNotificationName:Chat_kWebSocketdidReceiveMessageNote object:message];
        }else if ([type containsString:Type_History]){
            
//            K_LineDataModel * KL = [K_LineDataModel sharedK_LineDataModel];
//            NSArray * history = dic[@"history"];
//
//            for (NSDictionary * temp in history) {
//
//            }
            
        
            
            
        }else if([type containsString:@"kline.M1"]){
            
            K_LineDataModel * KL = [K_LineDataModel sharedK_LineDataModel];
            NSArray * kline = dic[@"kline"];
           
            KL.M1_KLineGroupModel = [Y_KLineGroupModel objectWithArray:kline];

            
        }else if ([type containsString:@"kline.M5"]){
            K_LineDataModel * KL = [K_LineDataModel sharedK_LineDataModel];
            NSArray * kline = dic[@"kline"];
            
            KL.M5_KLineGroupModel = [Y_KLineGroupModel objectWithArray:kline];
            
            [[NSNotificationCenter defaultCenter] postNotificationName:Kline_kWebSocketdidReceiveMessageNote object:message];
            
        }else if ([type containsString:@"kline.M60"]){
            K_LineDataModel * KL = [K_LineDataModel sharedK_LineDataModel];
            NSArray * kline = dic[@"kline"];
            
            KL.M60_KLineGroupModel = [Y_KLineGroupModel objectWithArray:kline];
            
            [[NSNotificationCenter defaultCenter] postNotificationName:Kline_kWebSocketdidReceiveMessageNote object:message];
            
        }
    }
}

#pragma mark - **************** setter getter
- (SRReadyState)socketReadyState{
    return self.socket.readyState;
}

-(void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
+ (NSDictionary *)dictionaryWithJsonString:(NSString *)jsonString
{
    if (jsonString == nil) {
        return nil;
    }
    
    NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
    NSError *err;
    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData
                                                        options:NSJSONReadingMutableContainers
                                                          error:&err];
    if(err)
    {
        NSLog(@"json解析失败：%@",err);
        return nil;
    }
    return dic;
}


#pragma mark-排序算法
-(void)sortingForSelectWithArray:(NSMutableArray *)array{
    int i,j,k;
    if (array.count<2) {
        return ;
    }
    for (i = 0; i<=array.count-1; i++) {
        k = i;
        for (j = i+1; j<=array.count-1; j++) {
            LTradeModel * k_temp = array[k];
            LTradeModel * j_temp = array[j];
            if ([k_temp.price doubleValue] >[j_temp.price doubleValue]) {
                k = j;
            }
        }
        if (k!=i) {
            id temp = array[i];
            array[i]= array[k];
            array[k]= temp;
        }
    }
}



@end

